package client;

import constants.GameConstants;
import java.util.ArrayList;
import java.util.List;

import provider.MapleData;
import provider.MapleDataTool;
import server.MapleStatEffect;
import server.Randomizer;
import server.life.Element;
import tools.Pair;

public class Skill {

    private String name = "", psdDamR = "";
    private final List<MapleStatEffect> effects = new ArrayList<MapleStatEffect>();
    private List<MapleStatEffect> pvpEffects = null;
    private List<Integer> animation = null;
    private final List<Pair<Integer, Byte>> requiredSkill = new ArrayList<Pair<Integer, Byte>>();
    private Element element = Element.NEUTRAL;
    private int id, animationTime = 0, masterLevel = 0, maxLevel = 0, delay = 0, trueMax = 0, eventTamingMob = 0, skillType = 0, psd = 0, psdSkill = 0;
    private boolean invisible = false, chargeskill = false, timeLimited = false, combatOrders = false, pvpDisabled = false, magic = false, casterMove = false, pushTarget = false, pullTarget = false;

    public Skill(final int id) {
        super();
        this.id = id;
    }

    public void setName(final String name) {
        this.name = name;
    }

    public int getId() {
        return id;
    }

    public String getName() {
        return name;
    }

    public static final Skill loadFromData(final int id, final MapleData data, final MapleData delayData) {
        Skill ret = new Skill(id);

        boolean isBuff = false;
        final int skillType = MapleDataTool.getInt("skillType", data, -1);
        final String elem = MapleDataTool.getString("elemAttr", data, null);
        if (elem != null) {
            ret.element = Element.getFromChar(elem.charAt(0));
        }
        ret.skillType = skillType;
        ret.invisible = MapleDataTool.getInt("invisible", data, 0) > 0;
        ret.timeLimited = MapleDataTool.getInt("timeLimited", data, 0) > 0;
        ret.combatOrders = MapleDataTool.getInt("combatOrders", data, 0) > 0;
        ret.masterLevel = MapleDataTool.getInt("masterLevel", data, 0);
        ret.psd = MapleDataTool.getInt("psd", data, 0);
        if (ret.psd == 1) {
            final MapleData psdskill = data.getChildByPath("psdSkill");
            if (psdskill != null) {
                ret.psdSkill = Integer.parseInt(data.getChildByPath("psdSkill").getChildren().get(0).getName());
            }
        }
        if ((id == 22111001 || id == 22140000 || id == 22141002)) {
            ret.masterLevel = 5; //hack
        }
        ret.eventTamingMob = MapleDataTool.getInt("eventTamingMob", data, 0);
        final MapleData inf = data.getChildByPath("info");
        if (inf != null) {
            ret.pvpDisabled = MapleDataTool.getInt("pvp", inf, 1) <= 0;
            ret.magic = MapleDataTool.getInt("magicDamage", inf, 0) > 0;
            ret.casterMove = MapleDataTool.getInt("casterMove", inf, 0) > 0;
            ret.pushTarget = MapleDataTool.getInt("pushTarget", inf, 0) > 0;
            ret.pullTarget = MapleDataTool.getInt("pullTarget", inf, 0) > 0;
        }
        final MapleData effect = data.getChildByPath("effect");
        switch (skillType) {
            case 2:
                isBuff = true;
                break;
            case 3:
                //final attack
                ret.animation = new ArrayList<Integer>();
                ret.animation.add(0);
                isBuff = effect != null;
                break;
            default:
                MapleData action_ = data.getChildByPath("action");
                final MapleData hit = data.getChildByPath("hit");
                final MapleData ball = data.getChildByPath("ball");
                boolean action = false;
                if (action_ == null) {
                    if (data.getChildByPath("prepare/action") != null) {
                        action_ = data.getChildByPath("prepare/action");
                        action = true;
                    }
                }   isBuff = effect != null && hit == null && ball == null;
                if (action_ != null) {
                    String d = null;
                    if (action) { //prepare
                        d = MapleDataTool.getString(action_, null);
                    } else {
                        d = MapleDataTool.getString("0", action_, null);
                    }
                    if (d != null) {
                        isBuff |= d.equals("alert2");
                        final MapleData dd = delayData.getChildByPath(d);
                        if (dd != null) {
                            for (MapleData del : dd) {
                                ret.delay += Math.abs(MapleDataTool.getInt("delay", del, 0));
                            }
                            if (ret.delay > 30) { //then, faster(2) = (10+2)/16 which is basically 3/4
                                ret.delay = (int) Math.round(ret.delay * 11.0 / 16.0); //fastest(1) lolol
                                ret.delay -= (ret.delay % 30); //round to 30ms
                            }
                        }
                        if (SkillFactory.getDelay(d) != null) { //this should return true always
                            ret.animation = new ArrayList<Integer>();
                            ret.animation.add(SkillFactory.getDelay(d));
                            if (!action) {
                                for (MapleData ddc : action_) {
                                    if (!MapleDataTool.getString(ddc, d).equals(d)) {
                                        String c = MapleDataTool.getString(ddc);
                                        if (SkillFactory.getDelay(c) != null) {
                                            ret.animation.add(SkillFactory.getDelay(c));
                                        }
                                    }
                                }
                            }
                        }
                    }
                }   switch (id) {
                    case 2301002: // heal is alert2 but not overtime...
                    case 2111003: // poison mist
                    case 12111005: // Flame Gear
                    case 22161003: //Evan's Recovery Aura
                    case 32121006:
                    case 11076:
                    case 2111002: // explosion
                    case 4211001: // chakra
                    case 2121001: // Big bang
                    case 2221001: // Big bang
                    case 2321001: // Big bang
                    case 1076:
                    case 8001000:
                        isBuff = false;
                        break;
                    case 23101003:
                        
                    case 4111009:
                    case 14111007:
                    case 3121002:
                    case 3221002:
                    case 5311004:
                    case 5121015:
                    case 5211009:
                    case 24111002:
                    case 1004: // monster riding
                    case 20031209: //Judgement Draw I
                    case 20031210: //Judgement Draw II
                    case 10001004:
                    case 20001004:
                    case 20011004:
                    case 80001000:
                        
                    case 80001079:
                    case 80001080:
                    case 80001081:
                        
                    case 1026:
                    case 10001026:
                    case 20001026:
                    case 20011026:
                    case 20021026:
                    case 30001026:
                    case 30011026:
                    case 93:
                    case 10000093:
                    case 20000093:
                    case 20010093:
                    case 20020093:
                    case 30000093:
                    case 30010093:
                    case 9101004: // hide is a buff -.- atleast for us o.o"
                    case 1111002: // combo
                    case 4211003: // pickpocket
                    case 4111001: // mesoup
                    case 15111002: // Super Transformation
                    case 5111005: // Transformation
                    case 5121003: // Super Transformation
                    case 13111005: // Alabtross
                    case 21000000: // Aran Combo
                    case 21101003: // Body Pressure
                    case 5211001: // Pirate octopus summon
                    case 5211002:
                    case 5220002: // wrath of the octopi
                    case 5001005: //dash
                    case 15001003:
                    case 5211006: //homing beacon
                    case 5220011: //bullseye
                    case 5110001: //energy charge
                    case 15100004:
                    case 5121009: //speed infusion
                    case 15111005:
                        
                    case 22121001: //high wisdom
                    case 22131001: //magic shield
                    case 22131002: // elemental reset
                    case 22141002: //magic booster
                    case 2311006: //magic booster
                    case 22151002: //killer wing
                    case 22151003: //magic resist
                    case 22161002: //imprint
                    case 22171000: //maple warrior
                    case 22171004: //hero will
                    case 5721002: //hero will
                    case 22181000: //onyx blessing
                    case 22181004:
                    case 22161004:
                    case 22181003: //soul stone
                        //case 22121000:
                        //case 22141003:
                        //case 22151001:
                        //case 22161002:
                    case 4331003: //owl spirit
                    case 15101006: //spark
                    case 15111006: //spark
                    case 4321000: //tornado spin
                    case 1320009: //beholder's buff.. passive
                    case 35120000:
                    case 35001002: //TEMP. mech
                    case 9001004: // hide
                    case 4341002:
                        
                    case 32001003: //dark aura
                    case 32120000:
                    case 32111012: //blue aura
                    case 32110000:
                    case 32101003: //yellow aura
                    case 32120001:
                    case 35101007: //perfect armor
                    case 35121006: //satellite safety
                    case 35001001: //flame
                    case 35101009:
                    case 35121005: //missile
                    case 35121013: // adv siege
                    case 35111004: //siege
                    case 33111003: //puppet ?
                    case 1211009:
                    case 1111007:
                    case 1311007: //magic,armor,atk crash
                    case 32121003: //twister
                    case 5111007:
                    case 5211007:
                    case 15111011://dice
                    case 5311005:
                    case 5320007:
                    case 5720005:
                    case 35111013: //dice
                    case 5711011: //dice
                    case 32111006:
                    case 5120011:
                    case 5220012:
                    case 1220013:
                    case 33101006: //jaguar oshi
                    case 32110007:
                    case 32110008:
                    case 32110009:
                    case 32111005:
                    case 31121005:
                    case 35121003:
                    case 35121009:
                    case 35121010:
                    case 35111005:
                    case 35111001:
                    case 35111010:
                    case 35111009:
                    case 35111011:
                    case 35111002:
                    case 35101005:
                    case 3120006:
                    case 3220005:
                    case 2121009:
                    case 2120010:
                    case 2221009:
                    case 2220010:
                    case 2321010:
                    case 2320011:
                    case 5321003:
                    case 5321004:
                    case 80001089:
                    case 24101005: // booster
                    case 5701005: // gun booster
                    case 24121009: // hero's will
                    case 24121008: // maple warrior
                    case 5711001: // turret
                        isBuff = true;
                        break;
                }   break;
        }
        ret.chargeskill = data.getChildByPath("keydown") != null;
        //some skills have old system, some new		
        final MapleData level = data.getChildByPath("common");
        if (level != null) {
            ret.maxLevel = MapleDataTool.getInt("maxLevel", level, 1); //10 just a failsafe, shouldn't actually happens
            ret.psdDamR = MapleDataTool.getString("damR", level, ""); //for the psdSkill tag
            ret.trueMax = ret.maxLevel + (ret.combatOrders ? 2 : 0);
            for (int i = 1; i <= ret.trueMax; i++) {
                ret.effects.add(MapleStatEffect.loadSkillEffectFromData(level, id, isBuff, i, "x"));
                //ret.effects.add(MapleStatEffect.loadSkillEffectFromData(level, id, isBuff, i, "t"));
            }

        } else {
            for (final MapleData leve : data.getChildByPath("level")) {
                ret.effects.add(MapleStatEffect.loadSkillEffectFromData(leve, id, isBuff, Byte.parseByte(leve.getName()), null));
            }
            ret.maxLevel = ret.effects.size();
            ret.trueMax = ret.effects.size();
        }
        final MapleData level2 = data.getChildByPath("PVPcommon");
        if (level2 != null) {
            ret.pvpEffects = new ArrayList<MapleStatEffect>();
            for (int i = 1; i <= ret.trueMax; i++) {
                ret.pvpEffects.add(MapleStatEffect.loadSkillEffectFromData(level2, id, isBuff, i, "x"));
            }
        }
        final MapleData reqDataRoot = data.getChildByPath("req");
        if (reqDataRoot != null) {
            reqDataRoot.getChildren().forEach((reqData) -> {
                ret.requiredSkill.add(new Pair<Integer, Byte>(Integer.parseInt(reqData.getName()), (byte) MapleDataTool.getInt(reqData, 1)));
            });
        }
        ret.animationTime = 0;
        if (effect != null) {
            for (final MapleData effectEntry : effect) {
                ret.animationTime += MapleDataTool.getIntConvert("delay", effectEntry, 0);
            }
        }
        return ret;
    }

    public MapleStatEffect getEffect(final int level) {
        if (effects.size() < level) {
            if (effects.size() > 0) { //incAllskill
                return effects.get(effects.size() - 1);
            }
            return null;
        } else if (level <= 0) {
            return effects.get(0);
        }
        return effects.get(level - 1);
    }

    public MapleStatEffect getPVPEffect(final int level) {
        if (pvpEffects == null) {
            return getEffect(level);
        }
        if (pvpEffects.size() < level) {
            if (pvpEffects.size() > 0) { //incAllskill
                return pvpEffects.get(pvpEffects.size() - 1);
            }
            return null;
        } else if (level <= 0) {
            return pvpEffects.get(0);
        }
        return pvpEffects.get(level - 1);
    }

    public int getSkillType() {
        return skillType;
    }

    public List<Integer> getAllAnimation() {
        return animation;
    }

    public int getAnimation() {
        if (animation == null) {
            return -1;
        }
        return animation.get(Randomizer.nextInt(animation.size()));
    }

    public boolean isPVPDisabled() {
        return pvpDisabled;
    }

    public boolean isChargeSkill() {
        return chargeskill;
    }

    public boolean isInvisible() {
        return invisible;
    }

    public boolean hasRequiredSkill() {
        return requiredSkill.size() > 0;
    }

    public int getPsdSkill() {
        return psdSkill;
    }

    public int getPsd() {
        return psd;
    }

    public String getPsdDamR() {
        return psdDamR;
    }

    public List<Pair<Integer, Byte>> getRequiredSkills() {
        return requiredSkill;
    }

    public int getMaxLevel() {
        return maxLevel;
    }

    public int getTrueMax() {
        return trueMax;
    }

    public boolean combatOrders() {
        return combatOrders;
    }

    public boolean canBeLearnedBy(int job) {
        int jid = job;
        int skillForJob = id / 10000;
        if (skillForJob == 2001) {
            return GameConstants.isEvan(job); //special exception for beginner -.-
        } else if (skillForJob == 0) {
            return GameConstants.isAdventurer(job); //special exception for beginner
        } else if (skillForJob == 1000) {
            return GameConstants.isKOC(job); //special exception for beginner
        } else if (skillForJob == 2000) {
            return GameConstants.isAran(job); //special exception for beginner
        } else if (skillForJob == 3000) {
            return GameConstants.isResist(job); //special exception for beginner
        } else if (skillForJob == 1) {
            return GameConstants.isCannon(job); //special exception for beginner
        } else if (skillForJob == 3001) {
            return GameConstants.isDemon(job); //special exception for beginner
        } else if (skillForJob == 2002) {
            return GameConstants.isMercedes(job); //special exception for beginner
        } else if (skillForJob == 508) {
            return GameConstants.isJett(job); //special exception for beginner
        } else if (skillForJob == 2003) {
            return GameConstants.isPhantom(job); //special exception for beginner
        } else if (jid / 100 != skillForJob / 100) { // wrong job
            return false;
        } else if (jid / 1000 != skillForJob / 1000) { // wrong job
            return false;
        } else if (GameConstants.isPhantom(skillForJob) && !GameConstants.isPhantom(job)) {
            return false;
        } else if (GameConstants.isJett(skillForJob) && !GameConstants.isJett(job)) {
            return false;
        } else if (GameConstants.isCannon(skillForJob) && !GameConstants.isCannon(job)) {
            return false;
        } else if (GameConstants.isDemon(skillForJob) && !GameConstants.isDemon(job)) {
            return false;
        } else if (GameConstants.isAdventurer(skillForJob) && !GameConstants.isAdventurer(job)) {
            return false;
        } else if (GameConstants.isKOC(skillForJob) && !GameConstants.isKOC(job)) {
            return false;
        } else if (GameConstants.isAran(skillForJob) && !GameConstants.isAran(job)) {
            return false;
        } else if (GameConstants.isEvan(skillForJob) && !GameConstants.isEvan(job)) {
            return false;
        } else if (GameConstants.isMercedes(skillForJob) && !GameConstants.isMercedes(job)) {
            return false;
        } else if (GameConstants.isResist(skillForJob) && !GameConstants.isResist(job)) {
            return false;
        } else if ((jid / 10) % 10 == 0 && (skillForJob / 10) % 10 > (jid / 10) % 10) { // wrong 2nd job
            return false;
        } else if ((skillForJob / 10) % 10 != 0 && (skillForJob / 10) % 10 != (jid / 10) % 10) { //wrong 2nd job
            return false;
        } else if (skillForJob / 10 > jid / 10) { // wrong 3rd/4th job
            return false;
        }
        return true;
    }

    public boolean isTimeLimited() {
        return timeLimited;
    }

    public boolean isFourthJobSkill(int skillid) {
        switch (skillid / 10000) {
            case 112:
            case 122:
            case 132:
            case 212:
            case 222:
            case 232:
            case 312:
            case 322:
            case 412:
            case 422:
            case 512:
            case 522:
                return true;
        }
        return false;
    }

    public boolean isThirdJobSkill(int skillid) {
        switch (skillid / 10000) {
            case 111:
            case 121:
            case 131:
            case 211:
            case 221:
            case 231:
            case 311:
            case 321:
            case 411:
            case 421:
            case 511:
            case 521:
                return true;
        }
        return false;
    }

    public boolean isSecondJobSkill(int skillid) {
        switch (skillid / 10000) {
            case 110:
            case 120:
            case 130:
            case 210:
            case 220:
            case 230:
            case 310:
            case 320:
            case 410:
            case 420:
            case 510:
            case 520:
                return true;
        }
        return false;
    }

    public boolean isFourthJob() {
        switch (id) { // I guess imma make an sql table to store these, so that we could max them all out.
            case 3220010:
            case 3120011:
            case 33120010:
            case 5220014:
            case 23120011:
            case 23121008:
            case 33121005:
            //case 32120009:
            case 5321006:
            case 21120011:
            case 22181004:
            case 4340010:
            case 4320005:
            case 5720008:
            case 5120012:
            case 5320007:
                //case 24121004://Aria Phantom skill
                return false;
        }
        //resurrection has master level while ult.strafe does not.. wtf, impossible to tell from WZ
        if ((id / 10000) == 2312) { //all 10 skills.
            return true;
        }
        if (id == 24121009 || id == 24121010) { //LOL WTF U LOSER
            return true;
        }
        if ((getMaxLevel() <= 15 && !invisible && getMasterLevel() <= 0)) {
            return false;
        }
        if (id / 10000 >= 2210 && id / 10000 < 3000) { //evan skill
            return ((id / 10000) % 10) >= 7 || getMasterLevel() > 0;
        }
        if (id / 10000 >= 430 && id / 10000 <= 434) { //db skill
            return ((id / 10000) % 10) == 4 || getMasterLevel() > 0;
        }
        return ((id / 10000) % 10) == 2 && id < 90000000 && !isBeginnerSkill();
    }

    public Element getElement() {
        return element;
    }

    public int getAnimationTime() {
        return animationTime;
    }

    public int getMasterLevel() {
        return masterLevel;
    }

    public int getDelay() {
        return delay;
    }

    public int getTamingMob() {
        return eventTamingMob;
    }

    public boolean isBeginnerSkill() {
        int jobId = id / 10000;
        return GameConstants.isBeginnerJob(jobId);
    }

    public boolean isMagic() {
        return magic;
    }

    public boolean isMovement() {
        return casterMove;
    }

    public boolean isPush() {
        return pushTarget;
    }

    public boolean isPull() {
        return pullTarget;
    }

    public boolean isSpecialSkill() {
        int jobId = id / 10000;
        return jobId == 900 || jobId == 800 || jobId == 9000 || jobId == 9200 || jobId == 9201 || jobId == 9202 || jobId == 9203 || jobId == 9204;
    }

    public boolean isGMSkill(int skillId) {
        switch (skillId) { //get all the gm skill ids
            case 8001000:
            case 8001001:
            case 9001000:
            case 9001001:
            case 9001002:
            case 9101000:
            case 9101001:
            case 9101002:
            case 9101003:
            case 9101004:
            case 9101005:
            case 9101006:
            case 9101007:
            case 9101008:
                return true;
            default:
                return false;
        }
    }

    public boolean isNormalSkill(Skill skillId, boolean isGM) {
        return isNormalSkill(skillId.getId(), isGM);
    }

    public boolean isNormalSkill(int skillId) {
        return isNormalSkill(skillId, false);
    }

    public boolean isNormalSkill(int skillId, boolean isGM) {
        if (isGMSkill(skillId) && !isGM) {
            return false;
        }
        switch (skillId) { //Working Skills
            case 0000012:
            case 0000073:
            case 0001000:
            case 0001001:
            case 0001002:
            case 10000012:
            case 10000073:
            case 80001089:
            case 10001000:
            case 10001001:
            case 10001002:
            case 20000012:
            case 20000073:
            case 20001000:
            case 20001001:
            case 20010012:
            case 20010073:
            case 20011000:
            case 20011002:
            case 20020012:
            case 20020073:
            case 20030012:
            case 20030073:
            case 20031203:
            case 20031204:
            case 20031205:
            case 20031206:
            case 20031207:
            case 20031208:
            case 20031209:
            case 20031210:
            case 30000012:
            case 30000073:
            case 30010012:
            case 30010073:
            case 50000012:
            case 50000073:
            case 50001000:
            case 50001001:
            case 50001002:
                return true;

            case 1000007:
            case 2000007:
            case 3000006:
            case 4000010:
            case 5000006:
            case 5001008:
            case 5001009:
            case 5010004:
            case 5010005:
            case 5081010:
            case 5081011:
            case 5090006:
            case 5300007:
            case 5310011:
            case 5320011:
            case 5710012:
            case 11000006:
            case 11111008:
            case 12000006:
            case 13000005:
            case 13110009:
            case 14000006:
            case 14111010:
            case 15000005:
            case 15001007:
            case 15111011:
            case 21000005:
            case 21120009:
            case 21120010:
            case 22000002:
            case 23000004:
            case 23101007:
            case 24000004:
            case 24121010:
            case 31000005:
            case 31001006:
            case 31001007:
            case 31001008:
            case 31100007:
            case 31120011:
            case 31121010:
            case 32000012:
            case 32110007:
            case 32110008:
            case 32110009:
            case 33000004:
            case 35000005:
            case 35121011:
                return false;
            default:
        return !((skillId >= 70000000 || skillId < 10000) || (skillId > 10000000 && skillId < 10019999) || (skillId > 20000000 && skillId < 20039999) || (skillId > 30000000 && skillId < 30019999) || (skillId > 50000000 && skillId < 50009999));
        }

    }
}
